package com.ias.agdis_r.util.date;

import java.security.InvalidParameterException;

/**
 * Created by qibo.yu on 2015/10/16.
 * 网关消息解析器
 */
public class SocketMessageParser {

    public interface MessageListener {
        /**
         * 新消息到达
         *
         * @param msg
         */
        void onMessageArrived(String msg);
    }

    /** 未处理消息片段缓存 */
    private String temp = "";

    private MessageListener listener;

    /** 消息开始标记 */
    private final String startTag;
    /** 消息结束标记 */
    private final String endTag;

    /** 消息开始标记长度 */
    private final int startTagLen;
    /** 消息结束标记长度 */
    private final int endTagLen;

    /** 连续消息连接处字符 */
    private final String splitTag;

    public SocketMessageParser(String messageStartTag, String messageEndTag, MessageListener messageListener) {

        checkTagsParam(messageStartTag, messageEndTag);

        this.startTag = messageStartTag;
        this.endTag = messageEndTag;
        this.listener = messageListener;

        this.splitTag = this.endTag + this.startTag;
        this.startTagLen = this.startTag.length();
        this.endTagLen = this.endTag.length();
    }

    /**
     * 使用默认开始标记“##”，结束标记“@@”
     * @param messageListener
     */
    public SocketMessageParser(MessageListener messageListener){
        this("##","@@",messageListener);
    }

    private void checkTagsParam(String startTag, String endTag) {

        if (null == startTag || "".equals(startTag)) {
            throw new InvalidParameterException("messageStartTag can not be empty");
        } else if (null == endTag || "".equals(endTag)) {
            throw new InvalidParameterException("messageEndTag can not be empty");
        } else if (startTag.equals(endTag)) {
            throw new InvalidParameterException("messageStartTag and messageEndTag can not equals each other.");
        }

    }

    /**
     * 推送消息段到解析器处理
     *
     * @param socketLine
     */
    public void pushLine(final String socketLine) {
        // 空段不处理
        if (null == socketLine || socketLine.length() == 0) {
            return;
        }

        // 将上一段不完整消息拼接到开头，保证当前处理消息段开头完整，并解析上一段未处理消息段
        String line = temp + socketLine;

        String msgLine = "";

        if (hasMoreLine(line)) {

            // 只有一条不完整，直接写入缓存等待下一次解析
            if (isSingleHalfMsg(line)) {
                temp = line;
                return;
            }

            // 最后一条完整消息尾位置
            int index = line.lastIndexOf(startTag);

            // 截取完整消息组
            msgLine = line.substring(0, index);
            // 缓存末尾不完整消息段
            temp = line.substring(index);

        } else {
            temp = "";
            msgLine = line;
        }

        // 解析消息组
        String[] msgs = parsMessages(msgLine);

        // 执行回调
        for (String msg : msgs) {
            opreateMsg(msg);
        }
    }

    /**
     * 处理消息
     */
    private void opreateMsg(String msg) {
        if (listener != null) {
            listener.onMessageArrived(msg);
        }

    }

    /**
     * @return 如果消息段只包含一条消息且不完整，返回true
     */
    private boolean isSingleHalfMsg(String line) {
        return !line.endsWith(endTag) && line.contains(splitTag);
    }

    /**
     * 将消息段解析为消息数组
     *
     * @param msgs
     *            消息段，如 ："##***@@","##***@@##.*@@"
     * @return 消息内容数组，如：[***, .*]
     */
    private String[] parsMessages(String msgs) {
        if (msgs.startsWith(startTag) && msgs.endsWith(endTag)) {
            msgs = msgs.substring(0, msgs.length() - endTagLen).substring(startTagLen);
            return msgs.split(splitTag);
        } else {
            throw new InvalidParameterException(
                    "msgs should start with " + startTag + " and end with " + endTag + ". current msgs: " + msgs);
        }

    }

    /**
     * 消息段最后一条消息是否完整
     *
     * @param line
     *            消息段
     * @return 完整则返回true
     */
    private boolean hasMoreLine(String line) {
        // 以endTag结尾代表最后一条消息是完整的
        return !line.endsWith(endTag);
    }

}
